import { PrismaClientKnownRequestError, PrismaClientUnknownRequestError } from '@prisma/client/runtime/library'

// 重试配置
export interface RetryConfig {
  maxRetries: number
  baseDelay: number
  maxDelay: number
  backoffMultiplier: number
  retryableErrors: string[]
}

const defaultRetryConfig: RetryConfig = {
  maxRetries: 3,
  baseDelay: 1000,
  maxDelay: 10000,
  backoffMultiplier: 2,
  retryableErrors: [
    'P2024', // Connection pool timeout
    'P2034', // Transaction failed due to a write conflict
    'P1001', // Can't reach database server
    'P1002', // Database server doesn't exist
    'P1003', // Database does not exist
    'P1008', // Operations timed out
    'P1009', // Database already exists
    'P1010', // User was denied access
    'P1011', // Error opening a TLS connection
    'P1012', // P1012
    'P1013', // The provided database string is invalid
    'P1014', // The underlying table does not exist
    'P1015', // Your Prisma schema is using features that are not supported
    'P1016', // Your raw query had an incorrect number of parameters
    'P1017', // Server has closed the connection
  ]
}

// 指数退避延迟计算
function calculateDelay(attempt: number, config: RetryConfig): number {
  const delay = config.baseDelay * Math.pow(config.backoffMultiplier, attempt - 1)
  return Math.min(delay, config.maxDelay)
}

// 检查错误是否可重试
function isRetryableError(error: any, config: RetryConfig): boolean {
  if (error instanceof PrismaClientKnownRequestError) {
    return config.retryableErrors.includes(error.code)
  }
  
  if (error instanceof PrismaClientUnknownRequestError) {
    // 检查错误消息中是否包含可重试的关键词
    const message = error.message.toLowerCase()
    return message.includes('timeout') || 
           message.includes('connection') || 
           message.includes('network') ||
           message.includes('econnreset') ||
           message.includes('enotfound')
  }
  
  // 检查其他类型的网络错误
  if (error.code === 'ECONNRESET' || 
      error.code === 'ENOTFOUND' || 
      error.code === 'ETIMEDOUT' ||
      error.code === 'ECONNREFUSED') {
    return true
  }
  
  return false
}

// 重试装饰器
export function withRetry<T extends any[], R>(
  fn: (...args: T) => Promise<R>,
  config: Partial<RetryConfig> = {}
): (...args: T) => Promise<R> {
  const finalConfig = { ...defaultRetryConfig, ...config }
  
  return async (...args: T): Promise<R> => {
    let lastError: any
    
    for (let attempt = 1; attempt <= finalConfig.maxRetries; attempt++) {
      try {
        return await fn(...args)
      } catch (error) {
        lastError = error
        
        // 如果不是最后一次尝试且错误可重试
        if (attempt < finalConfig.maxRetries && isRetryableError(error, finalConfig)) {
          const delay = calculateDelay(attempt, finalConfig)
          const errorMessage = error instanceof Error ? error.message : String(error)
          console.warn(`Retry attempt ${attempt}/${finalConfig.maxRetries} after ${delay}ms. Error:`, errorMessage)
          
          await new Promise(resolve => setTimeout(resolve, delay))
          continue
        }
        
        // 如果不可重试或已达到最大重试次数，抛出错误
        throw error
      }
    }
    
    throw lastError
  }
}

// 数据库操作重试包装器
export class DatabaseRetryWrapper {
  private config: RetryConfig
  
  constructor(config: Partial<RetryConfig> = {}) {
    this.config = { ...defaultRetryConfig, ...config }
  }
  
  async execute<T>(operation: () => Promise<T>): Promise<T> {
    return withRetry(operation, this.config)()
  }
  
  async executeTransaction<T>(operation: (tx: any) => Promise<T>): Promise<T> {
    const { getPrismaClient } = await import('./connection')
    const prisma = getPrismaClient()
    
    return withRetry(async () => {
      return await prisma.$transaction(operation)
    }, this.config)()
  }
}

// 熔断器模式
export class CircuitBreaker {
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED'
  private failureCount = 0
  private lastFailureTime = 0
  private successCount = 0
  
  constructor(
    private failureThreshold = 5,
    private recoveryTimeout = 60000,
    private halfOpenMaxCalls = 3
  ) {}
  
  async execute<T>(operation: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailureTime > this.recoveryTimeout) {
        this.state = 'HALF_OPEN'
        this.successCount = 0
      } else {
        throw new Error('Circuit breaker is OPEN')
      }
    }
    
    if (this.state === 'HALF_OPEN' && this.successCount >= this.halfOpenMaxCalls) {
      this.state = 'CLOSED'
      this.failureCount = 0
    }
    
    try {
      const result = await operation()
      this.onSuccess()
      return result
    } catch (error) {
      this.onFailure()
      throw error
    }
  }
  
  private onSuccess(): void {
    this.failureCount = 0
    if (this.state === 'HALF_OPEN') {
      this.successCount++
    }
  }
  
  private onFailure(): void {
    this.failureCount++
    this.lastFailureTime = Date.now()
    
    if (this.failureCount >= this.failureThreshold) {
      this.state = 'OPEN'
    }
  }
  
  getState(): string {
    return this.state
  }
  
  getMetrics() {
    return {
      state: this.state,
      failureCount: this.failureCount,
      successCount: this.successCount,
      lastFailureTime: this.lastFailureTime
    }
  }
}

// 全局熔断器实例
export const databaseCircuitBreaker = new CircuitBreaker()
